package licode.unisop.single.platform.infrastructure.gateway;

import cn.hutool.core.bean.BeanUtil;
import licode.unisop.provider.api.SiPlatformProvider;
import licode.unisop.provider.conf.SiAssetState;
import licode.unisop.provider.conf.SiAssetType;
import licode.unisop.provider.conf.SiClassifyType;
import licode.unisop.provider.info.*;
import licode.unisop.provider.utils.PluginUtil;
import licode.unisop.provider.utils.SiAssetUtil;
import licode.unisop.provider.utils.StrUtil;
import licode.unisop.provider.vo.*;
import licode.unisop.single.platform.application.entity.PlatAssetPropEntity;
import licode.unisop.single.platform.application.entity.PlatformEntity;
import licode.unisop.single.platform.application.gateway.*;
import licode.unisop.single.platform.application.info.*;
import licode.unisop.single.platform.infrastructure.utils.PageUtil;
import licode.unisop.types.util.IdGenUtil;
import licode.unisop.unilite.result.UniException;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 平台默认实现
 *
 * @author licode
 */
@Primary
@Service
public class SiPlatformProviderSingle implements SiPlatformProvider {
    @Resource
    private PlatformDao platformDao;

    @Resource
    private PlatAssetDao platAssetDao;

    @Resource
    private AuthPlatformDao authPlatformDao;

    @Resource
    private PlatAssetRelDao platAssetRelDao;

    @Resource
    private PlatAssetPropDao platAssetPropDao;

    private SiAssetEntity assetInfoToEntity(String ownerId, SiAssetBrief resource) {
        SiAssetEntity entity = new SiAssetEntity();

        entity.setId(resource.getId());
        entity.setParentId(resource.getParentId());
        entity.setRootId(resource.getRootId());
        entity.setCode(resource.getCode());
        entity.setOwnerId(ownerId);
        entity.setName(resource.getName());
        entity.setClassify(resource.getClassify());
        entity.setType(resource.getType());
        entity.setSubType(resource.getSubType());
        entity.setLevel(resource.getLevel());
        entity.setState(null == resource.getState() ? SiAssetState.AT_ACTIVATE : resource.getState());
        entity.setProps(resource.getProps());
        entity.setIsSystem(resource.getIsSystem());

        entity.setUpdateTime(LocalDateTime.now());

        return entity;
    }

    private PlatformEntity toPlatformEntity(SiPlatform platform) {
        PlatformEntity entity = new PlatformEntity();

        entity.setId(platform.getId());
        entity.setRealmId(platform.getRealmId());
        entity.setName(platform.getName());
        entity.setLabel("");
        entity.setSecret(platform.getSecret());
        entity.setSalt("");
        entity.setCanEdit(1);
        entity.setComment("");
        entity.setState(platform.getState());
        entity.setHomeUrl(platform.getHomeUrl());
        entity.setTicketUrl(platform.getTicketUrl());
        entity.setUpdateTime(platform.getUpdateTime());
        entity.setCreateTime(platform.getCreateTime());

        if (platform.hasProps()) {
            entity.setProps(platform.getProps());
            entity.setCanEdit(platform.getProps().getInt("canEdit", 1));
            entity.setUserShare(platform.getProps().getString("userShare", "none"));
        }

        return entity;
    }

    private SiPlatform fromPlatformEntity(PlatformEntity entity) {
        if (null == entity) {
            return null;
        }

        SiPlatform platform = buildPlatform(entity);
        if (null != entity.getProps()) {
            entity.getProps().forEach((name, val) -> platform.getProps().put(name, val));
        }

        platform.getProps().addProp("canEdit", entity.getCanEdit());
        platform.getProps().addProp("userShare", entity.getUserShare());

        return platform;
    }

    @NotNull
    private static SiPlatform buildPlatform(PlatformEntity entity) {
        SiPlatform platform = new SiPlatform();

        platform.setId(entity.getId());
        platform.setRealmId(entity.getRealmId());
        platform.setName(entity.getName());
        platform.setSecret(entity.getSecret());
        platform.setState(entity.getState());
        platform.setTicketUrl(entity.getTicketUrl());
        platform.setHomeUrl(entity.getHomeUrl());
        platform.setUpdateTime(entity.getUpdateTime());
        platform.setCreateTime(entity.getCreateTime());

        platform.setProps(new SiProps());
        return platform;
    }

    @Override
    public String getName() {
        return "默认";
    }

    @Override
    public void addPlatform(SiPlatform platform) {
        PlatformEntity entity = toPlatformEntity(platform);
        entity.setCreateTime(LocalDateTime.now());
        if (null != platformDao.getPlatformByName(platform.getName())) {
            throw new UniException(-1, "平台名称已存在");
        }
        platformDao.add(entity);
    }

    @Override
    public void updatePlatform(SiPlatform platform) {
        PlatformEntity entity = toPlatformEntity(platform);
        entity.setUpdateTime(LocalDateTime.now());
        PlatformEntity namePlat = platformDao.getPlatformByName(platform.getName());

        if (null != namePlat && !namePlat.getId().equals(platform.getId())) {
            throw new UniException(-1, "平台名称已存在");
        }
        platformDao.update(entity);
    }

    @Override
    public void updatePlatformRealm(SiPlatRealmUpdateIn platform) {
        platformDao.updatePlatformRealm(platform);
    }

    @Override
    public void grantPlatform(SiAuthPlatToUserIn item) {
        if (Integer.valueOf(1).equals(item.getGrant())) {
            authPlatformDao.addGrantPlatform(item);
        } else {
            authPlatformDao.removeGrantPlatform(item);
        }
    }

    private void initAndCheckAsset(SiAssetEntity entity) {
        if (PluginUtil.isBlank(entity.getClassify())) {
            if (SiAssetType.AT_ROLE.equals(entity.getType()) ||
                    SiAssetType.AT_MENU.equals(entity.getType())) {
                entity.setClassify("TREE");
            } else if (SiAssetType.AT_GROUP.equals(entity.getType())) {
                entity.setClassify("GROUP_TREE");
            } else {
                entity.setClassify("NORMAL");
            }
        }
        if (null == entity.getSubType()) {
            entity.setSubType("");
        }
        if (null == entity.getTerminalCode()) {
            entity.setTerminalCode("default");
        }
    }

    @Override
    public void removeAsset(SiAssetDel delInfo) {
        List<String> ids = findAssetSubTreeIds(SiAssetId.builder()
                .ownerId(delInfo.getPlatformId())
                .assetId(delInfo.getAssetId())
                .assetType(delInfo.getAssetType())
                .build());
        ids.add(delInfo.getAssetId());

        SiAssetRemove info = SiAssetRemove.builder()
                .platformId(delInfo.getPlatformId())
                .ids(ids)
                .build();

        platAssetDao.removeAssets(info);
        platAssetRelDao.removeAll(info);
    }

    @Override
    public void removeRelAsset(SiRemoveRelAsset delInfo) {
        if (null == delInfo.getAssetIds()) {
            delInfo.setAssetIds(new ArrayList<>());
        }
        if (null == delInfo.getTargetIds()) {
            delInfo.setTargetIds(new ArrayList<>());
        }

        if (delInfo.getTargetIds().isEmpty() && null != delInfo.getTargetId()) {
            delInfo.getTargetIds().add(delInfo.getTargetId());
        }

        if (delInfo.getAssetIds().isEmpty() && null != delInfo.getAssetId()) {
            delInfo.getAssetIds().add(delInfo.getAssetId());
        }

        platAssetRelDao.removeAssets(RemoveHoldAssetsIn.builder()
                .platformId(delInfo.getPlatformId())
                .holdIds(delInfo.getAssetIds())
                .holdType(delInfo.getAssetType())
                .assetIds(delInfo.getTargetIds())
                .assetType(delInfo.getTargetType())
                .build());
    }

    @Override
    public void removeAllRelAsset(SiRemoveAllRelAsset req) {
        platAssetRelDao.removeAllRelAsset(req);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class, timeout = 60)
    public void saveHoldAssets(SiSaveHoldAssets info) {
        String assetType = info.getAssetType();

        if (Integer.valueOf(1).equals(info.getSaveAll())) {
            platAssetRelDao.removeAssets(RemoveHoldAssetsIn.builder()
                    .platformId(info.getPlatformId())
                    .holdIds(Collections.singletonList(info.getHoldId()))
                    .holdType(info.getHoldType())
                    .assetType(assetType)
                    .build());
        }

        if (null != info.getAssets()) {
            List<SiAssetRelEntity> items = new ArrayList<>();
            info.getAssets().forEach(item -> {
                SiAssetRelEntity entity = new SiAssetRelEntity();
                entity.setId(IdGenUtil.buildRelationId());
                entity.setTerminalCode("default");
                entity.setOwnerId(info.getPlatformId());
                entity.setAssetOwner(info.getHoldOwner());
                entity.setAssetId(info.getHoldId());
                entity.setAssetType(info.getHoldType());
                entity.setRelationType("HOLD");
                entity.setTargetOwner(info.getAssetOwner());
                entity.setTargetId(item.getAssetId());
                entity.setTargetType(assetType);
                entity.setAssetSearch("");
                entity.setTargetSearch(item.getName() == null ? "" : item.getName());
                entity.setUpdateTime(LocalDateTime.now());
                entity.setCreateTime(entity.getUpdateTime());

                items.add(entity);
            });

            platAssetRelDao.addBatch(items);
        }
    }

    @Override
    @Transactional(rollbackFor = Throwable.class, timeout = 60)
    public void saveAssetHolds(SiSaveAssetHolds info) {
        if (null != info.getHolders() && !info.getHolders().isEmpty()) {
            String holdType = info.getHoldType();
            List<SiAssetRelEntity> items = new ArrayList<>();

            info.getHolders().forEach(item -> {
                SiAssetRelEntity entity = new SiAssetRelEntity();
                entity.setId(IdGenUtil.buildRelationId());
                entity.setTerminalCode("default");
                entity.setOwnerId(info.getPlatformId());
                entity.setAssetOwner(info.getHoldOwner());
                entity.setAssetId(item.getHoldId());
                entity.setAssetType(holdType);
                entity.setRelationType("HOLD");
                entity.setTargetOwner(info.getAssetOwner());
                entity.setTargetId(info.getAssetId());
                entity.setTargetType(info.getAssetType());
                entity.setAssetSearch(item.getName() == null ? "" : item.getName());
                entity.setTargetSearch("");
                entity.setUpdateTime(LocalDateTime.now());
                entity.setCreateTime(entity.getUpdateTime());

                items.add(entity);
            });

            if (Integer.valueOf(1).equals(info.getSaveAll())) {
                platAssetRelDao.removeHolders(RemoveAssetHoldsIn.builder()
                        .platformId(info.getPlatformId())
                        .assetId(info.getAssetId())
                        .assetType(info.getAssetType())
                        .holdType(holdType)
                        .build());
            }

            platAssetRelDao.addBatch(items);
        }
    }

    @Override
    public SiPlatform findPlatform(SiPlatIdFind findInfo) {
        return fromPlatformEntity(platformDao.getPlatform(findInfo));
    }

    @Override
    public SiAssetEntry lookupAsset(SiAssetId idFind) {
        SiAssetEntry entry = null;
        SiAsset asset = findAssetBase(idFind);
        if (null != asset) {
            entry = new SiAssetEntry();
            entry.setRelation(new ArrayList<>());
            entry.setAsset(asset);
            if (idFind.isSearchTree()) {
                fillAssetTree(idFind, asset);
            }
            if (idFind.isSearchRel()) {
                fillAssetRelation(idFind, asset, entry.getRelation());
            }
        }
        return entry;
    }

    @Override
    public SiAssetBrief lookupAssetBrief(SiAssetLookupIn lookupIn) {
        return entityToAssetBrief(platAssetDao.getPlatAsset(SiAssetId.builder()
                .assetId(lookupIn.getAssetId())
                .assetType(lookupIn.getAssetType())
                .ownerId(lookupIn.getOwnerId())
                .build()));

    }

    @Override
    public SiAssetBrief lookupAsseByCode(SiLookupAssetByCodeIn lookupIn) {
        return entityToAssetBrief(platAssetDao.lookupAsseByCode(lookupIn));
    }

    private SiAssetBrief entityToAssetBrief(SiAssetEntity asset) {
        return null == asset ? null : SiAssetBrief.builder()
                .id(asset.getId())
                .rootId(asset.getRootId())
                .parentId(asset.getParentId())
                .name(asset.getName())
                .code(asset.getCode())
                .type(asset.getType())
                .subType(asset.getSubType())
                .classify(asset.getClassify())
                .level(asset.getLevel())
                .state(asset.getState())
                .isSystem(asset.getIsSystem())
                .props(asset.getProps() == null ? null : SiProps.build(asset.getProps()))
                .build();
    }

    @Override
    public Long getSubAssetCount(SiAssetLookupIn lookupIn) {
        return platAssetDao.getSubAssetCount(lookupIn);
    }

    @Override
    public List<String> findAssetSubTreeIds(SiAssetId idFind) {
        List<String> subIds = new ArrayList<>();
        SiAsset asset = findAssetBase(idFind);

        if (null != asset && StrUtil.isNotBlank(asset.getRootId())) {
            List<SiAssetEntity> roots = platAssetDao.getPlatAssetsByRootId(SiRootId
                    .builder()
                    .platformId(idFind.getOwnerId())
                    .rootId(asset.getRootId())
                    .build());
            if (null != roots && !roots.isEmpty()) {
                List<SiAsset> assets = new ArrayList<>();
                roots.forEach(item -> assets.add(fillAssetBase(item)));
                handleSingleSubTree(asset, assets, subIds);
            }
        }

        return subIds;
    }

    @Override
    public List<SiAssetKey> findAssetKeys(SiAssetId idInfo) {
        List<SiAssetRelEntity> relList;

        relList = platAssetRelDao.getRelations(
                RelationIdInfo.builder()
                        .ownerId(idInfo.getOwnerId())
                        .assetId(idInfo.getAssetId())
                        .assetIds(idInfo.getAssetIds())
                        .assetType(idInfo.getAssetType())
                        .targetId(idInfo.getTargetId())
                        .targetIds(idInfo.getTargetIds())
                        .targetType(idInfo.getTargetType())
                        .build());
        if (null == relList) {
            relList = new ArrayList<>();
        }

        if (StrUtil.isBlank(idInfo.getTargetId()) && null == idInfo.getTargetIds()) {
            return relList.stream().map(item -> SiAssetKey.builder()
                            .id(item.getTargetId())
                            .type(item.getTargetType())
                            .build())
                    .collect(Collectors.toList());
        } else {
            return relList.stream().map(item -> SiAssetKey.builder()
                            .id(item.getAssetId())
                            .type(item.getAssetType())
                            .build())
                    .collect(Collectors.toList());
        }
    }

    @Override
    public SiAssetInfo getPlatAsset(SiPlatIdFind plInfo) {
        List<SiAssetEntity> assets;
        assets = platAssetDao.getPlatAssets(SiAssetIds.builder()
                .ownerId(plInfo.getPlatformId())
                .build());
        return handleAuthPlatAsset(plInfo, assets);
    }

    @Override
    public SiAssetInfo getAuthPlatAsset(SiPlatIdFind plInfo) {
        List<SiAssetEntity> assets;
        assets = platAssetDao.getAuthPlatAssets(SiAssetIds.builder()
                .ownerId(plInfo.getPlatformId())
                .build());
        return handleAuthPlatAsset(plInfo, assets);
    }

    public SiAssetInfo handleAuthPlatAsset(SiPlatIdFind plInfo, List<SiAssetEntity> assets) {
        SiAssetInfo assetInfo = new SiAssetInfo();
        assetInfo.setTargetId(plInfo.getPlatformId());

        if (assets != null && !assets.isEmpty()) {
            Map<String, SiAsset> holdAssets = new HashMap<>();
            Map<String, List<SiAsset>> treeAssets = new HashMap<>();

            assetInfo.setAssets(new HashMap<>());

            for (SiAssetEntity asset : assets) {
                List<SiAsset> roots;
                SiAsset siAsset = fillAssetBase(asset);

                if (plInfo.getFilterMode() != null) {
                    if (Integer.valueOf(1).equals(plInfo.getFilterMode())) {
                        siAsset.setState(null);
                    }
                    if (Integer.valueOf(1).equals(plInfo.getFilterMode()) ||
                            Integer.valueOf(2).equals(plInfo.getFilterMode())) {
                        if (siAsset.getProps() != null) {
                            siAsset.getProps().removeProp("updateTime");
                            siAsset.getProps().removeProp("createTime");
                        }
                    }
                }

                assetInfo.getAssets().put(asset.getId(), siAsset);

                if (SiClassifyType.ART_TREE.equals(asset.getClassify()) ||
                        SiClassifyType.ART_GROUP_TREE.equals(asset.getClassify())) {
                    roots = treeAssets.computeIfAbsent(asset.getRootId(), k -> new ArrayList<>());
                    roots.add(siAsset);
                }

                if (SiAssetType.AT_CLIENT.equals(asset.getType()) ||
                        SiAssetType.AT_ROLE.equals(asset.getType()) ||
                        SiAssetType.AT_GROUP.equals(asset.getType())) {
                    holdAssets.put(asset.getId(), siAsset);
                }
            }

            handleTreeAssets(assetInfo, treeAssets);
            assetInfo.setHolds(handleHoldAssets(SiRealmFind.builder()
                    .platformId(assetInfo.getTargetId())
                    .build(), assetInfo.getAssets(), holdAssets));
        }

        return assetInfo;
    }

    @Override
    public List<SiAssetBrief> findAssetList(AssetListInfo req) {
        return platAssetDao.findAssetList(req);
    }

    private List<SiAssetRelEntity> queryRelations(SiRealmFind find, List<String> ids) {
        if (ids.isEmpty()) {
            return new ArrayList<>();
        } else {
            return platAssetRelDao.getRelations(
                    RelationIdInfo.builder()
                            .ownerId(find.getPlatformId())
                            .assetIds(ids)
                            .build());
        }
    }

    private Map<String, List<SiAssetKey>> handleHoldAssets(SiRealmFind idFind,
                                                           Map<String, SiAsset> assets,
                                                           Map<String, SiAsset> holdAssets) {

        List<SiAssetRelEntity> relList;
        Map<String, List<SiAssetKey>> holdKeys = new HashMap<>();

        relList = queryRelations(idFind,
                holdAssets.values().stream().map(SiAsset::getId).collect(Collectors.toList()));

        if (!relList.isEmpty()) {
            relList.forEach(item -> {
                List<SiAssetKey> keyItems;
                SiAsset trgItem = assets.get(item.getTargetId());
                if (null != trgItem) {
                    keyItems = holdKeys.computeIfAbsent(item.getAssetId(), k -> new ArrayList<>());
                    keyItems.add(SiAssetKey.builder()
                            .id(trgItem.getId()).type(trgItem.getType()).build());
                }
            });
        }

        return holdKeys;
    }

    private void copyTreeAsset(SiAssetTKey parent, SiAsset siAsset) {
        parent.setParentId(siAsset.getParentId());
        parent.setId(siAsset.getId());
        parent.setType(siAsset.getType());
        parent.setLevel(siAsset.getLevel());
        if (siAsset.hasChildren()) {
            parent.setChildren(new ArrayList<>());
            for (SiAsset asset : siAsset.getChildren()) {
                SiAssetTKey keyNode = new SiAssetTKey();
                parent.getChildren().add(keyNode);
                copyTreeAsset(keyNode, asset);
            }
        }
    }

    private void handleTreeAssets(SiAssetInfo assetInfo, Map<String, List<SiAsset>> treeAssets) {
        assetInfo.setTrees(new ArrayList<>());
        treeAssets.forEach((key, item) -> {
            SiAsset siAsset = handleSingleTree(item);
            if (null != siAsset) {
                SiAssetTKey keyNode = new SiAssetTKey();
                copyTreeAsset(keyNode, siAsset);
                assetInfo.getTrees().add(keyNode);
            }
        });

        SiAssetUtil.sortAssetKeys(assetInfo.getTrees());
    }

    private SiAsset handleSingleTree(List<SiAsset> assets) {
        SiAsset root = null;
        for (SiAsset item : assets) {
            if (StrUtil.isBlank(item.getParentId())) {
                root = item;
                assets.remove(item);
                break;
            }
        }

        if (null != root) {
            handleSingleSubTree(root, assets, null);
        }

        return root;
    }

    private void handleSingleSubTree(SiAsset parent, List<SiAsset> assets, List<String> subIds) {
        Iterator<SiAsset> iter = assets.iterator();
        while (iter.hasNext()) {
            SiAsset asset = iter.next();
            if (parent.getId().equals(asset.getParentId())) {
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList<>());
                }
                if (null != subIds) {
                    subIds.add(asset.getId());
                }
                parent.getChildren().add(cloneAsset(asset));
                iter.remove();
            }
        }

        if (null != parent.getChildren()) {
            parent.getChildren().forEach(item -> handleSingleSubTree(item, assets, subIds));
        }
    }

    @Override
    public SiPageResult<SiPlatform> findPlatformPage(PlatPageInfo req) {
        return platformDao.findPage(req);
    }

    @Override
    public SiPageResult<String> findAuthPlatformPage(AuthPlatPageInfo req) {
        return authPlatformDao.findAuthPlatformPage(req);
    }

    @Override
    public void addAsset(SiSaveAssetIn assetIn) {
        SiAssetEntity entity = assetInfoToEntity(assetIn.getPlatformId(), assetIn.getBrief());

        if (PluginUtil.isBlank(entity.getRootId())) {
            if (StrUtil.isNotBlank(entity.getParentId())) {
                SiAssetEntity asset = platAssetDao.getPlatAsset(SiAssetId.builder()
                        .assetId(entity.getParentId())
                        .assetType(entity.getType())
                        .ownerId(entity.getOwnerId())
                        .build());
                if (null != asset) {
                    entity.setRootId(asset.getRootId());
                }
            }

            if (PluginUtil.isBlank(entity.getRootId())) {
                entity.setRootId(entity.getId());
            }
        }

        initAndCheckAsset(entity);

        assetIn.getBrief().setId(entity.getId());
        entity.setCreateTime(entity.getUpdateTime());
        platAssetDao.add(entity);
    }

    @Override
    public void updateAsset(SiSaveAssetIn assetIn) {
        SiAssetEntity entity = assetInfoToEntity(assetIn.getPlatformId(), assetIn.getBrief());

        if (PluginUtil.isNotBlank(assetIn.getBrief().getId()) &&
                platAssetDao.exist(assetIn.getBrief().getId())) {
            platAssetDao.update(entity);
        }
    }

    @Override
    public SiPageResult<SiAssetBrief> findAssetPage(AssetPageInfo req) {
        if (null != req.getFilterRelMode()) {
            if (StrUtil.isNotBlank(req.getFilterRelType()) &&
                    null != req.getFilterRelIds() &&
                    !req.getFilterRelIds().isEmpty()) {
                List<SiAssetRelEntity> relInfo;
                if (Integer.valueOf(1).equals(req.getFilterRelMode())) {
                    relInfo = platAssetRelDao.getRelations(RelationIdInfo.builder()
                            .ownerId(req.getPlatformId())
                            .assetIds(req.getFilterRelIds())
                            .assetType(req.getFilterRelType())
                            .targetType(req.getAssetType())
                            .build());
                } else {
                    relInfo = platAssetRelDao.getRelations(RelationIdInfo.builder()
                            .ownerId(req.getPlatformId())
                            .targetIds(req.getFilterRelIds())
                            .targetType(req.getFilterRelType())
                            .assetType(req.getAssetType())
                            .build());
                }

                if (null != relInfo && !relInfo.isEmpty()) {
                    if (null == req.getFilterAssets()) {
                        req.setFilterAssets(new ArrayList<>());
                    }
                    if (Integer.valueOf(1).equals(req.getFilterRelMode())) {
                        req.getFilterAssets().addAll(relInfo.stream()
                                .map(SiAssetRelEntity::getTargetId)
                                .collect(Collectors.toList()));
                    } else {
                        req.getFilterAssets().addAll(relInfo.stream()
                                .map(SiAssetRelEntity::getAssetId)
                                .collect(Collectors.toList()));
                    }
                }
            }
        }

        return platAssetDao.findAssetPage(req);
    }

    @Override
    public SiPageResult<SiAssetKey> findAssetKeysPage(SiAssetIdPage idInfo) {
        SiPageResult<SiAssetRelEntity> result = platAssetRelDao.getRelationsPage(
                RelationIdPage.builder()
                        .current(idInfo.getCurrent())
                        .pageSize(idInfo.getPageSize())
                        .relationId(RelationIdInfo.builder()
                                .ownerId(idInfo.getPlatformId())
                                .assetId(idInfo.getAssetId())
                                .assetType(idInfo.getAssetType())
                                .targetId(idInfo.getTargetId())
                                .targetType(idInfo.getTargetType())
                                .build())
                        .build());
        return convertToPage(result);
    }

    private List<SiAssetRelation> convertSiAssetRelation(List<SiAssetRelEntity> relList) {
        if (null != relList) {
            return relList.stream().map(item -> BeanUtil.copyProperties(item, SiAssetRelation.class))
                    .collect(Collectors.toList());
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    public SiPageResult<SiAssetRelation> findHoldRefAssetPage(HoldAssetPageInfo req) {
        return PageUtil.convertPage(platAssetRelDao.findHoldRefAssetPage(req), this::convertSiAssetRelation);
    }

    @Override
    public SiPageResult<SiAssetRelation> findAssetRefHoldPage(AssetHoldPageInfo req) {
        return PageUtil.convertPage(platAssetRelDao.findAssetRefHoldPage(req), this::convertSiAssetRelation);
    }

    @Override
    public SiPageResult<SiAssetBrief> findHoldAssetPage(HoldAssetPageInfo req) {
        SiPageResult<SiAssetRelEntity> result;
        result = platAssetRelDao.findHoldAssetPage(req);
        return fromRelEntityPage(req.getOwnerId(), req.getAssetType(),
                result, SiAssetRelEntity::getTargetId);
    }

    @Override
    public SiPageResult<SiAssetBrief> findAssetHoldPage(AssetHoldPageInfo req) {
        SiPageResult<SiAssetRelEntity> result;
        result = platAssetRelDao.findAssetHoldPage(req);
        if (null == result || null == result.getData() || result.getData().isEmpty()) {
            return null;
        } else {
            return fromRelEntityPage(req.getOwnerId(), req.getHoldType(),
                    result, SiAssetRelEntity::getAssetId);
        }
    }

    private SiPageResult<SiAssetBrief> fromRelEntityPage(String ownerId, String assertType,
                                                         SiPageResult<SiAssetRelEntity> result,
                                                         Function<? super SiAssetRelEntity, ? extends String> mapper) {
        if (null == result || null == result.getData() || result.getData().isEmpty()) {
            return null;
        } else {
            SiPageResult<SiAssetBrief> briefPage = new SiPageResult<>();
            briefPage.setCurrent(result.getCurrent());
            briefPage.setPageSize(result.getPageSize());
            briefPage.setCount(result.getCount());
            briefPage.setTotal(result.getTotal());
            briefPage.setData(platAssetDao.findAssetList(AssetListInfo.builder()
                    .platformId(ownerId)
                    .assetType(assertType)
                    .assetIds(result.getData().stream().map(mapper).collect(Collectors.toList()))
                    .build()));
            return briefPage;
        }
    }

    private SiPageResult<SiAssetKey> convertToPage(SiPageResult<SiAssetRelEntity> relPage) {
        SiPageResult<SiAssetKey> result = null;
        if (null != relPage) {
            result = new SiPageResult<>();
            result.setCurrent(relPage.getCurrent());
            result.setPageSize(relPage.getPageSize());
            result.setTotal(relPage.getTotal());
            result.setCount(relPage.getCount());
            result.setData(relPage.getData().stream().map(item -> SiAssetKey.builder()
                            .id(item.getTargetId())
                            .type(item.getTargetType())
                            .build())
                    .collect(Collectors.toList()));
        }

        return result;
    }

    private SiAsset cloneAsset(SiAsset asset) {
        SiAsset siAsset = BeanUtil.copyProperties(asset, SiAsset.class, "classify");
        if (siAsset == null) {
            return null;
        } else {
            return siAsset;
        }
    }

    private void fillAssetProps(SiAssetId idFind, SiAsset assetInfo) {
        List<PlatAssetPropEntity> props = platAssetPropDao.getAssetProps(idFind);
        if (null != props && !props.isEmpty()) {
            if (null == assetInfo.getProps()) {
                assetInfo.setProps(new SiProps());
            }

            props.forEach(item -> {
                if (!item.isEmpty()) {
                    assetInfo.getProps().addProp(item.getName(), item.getValidValue());
                }
            });
        }
    }

    public SiAsset findAssetBase(SiAssetId idFind) {
        SiAsset assetInfo = null;
        SiAssetEntity asset = platAssetDao.getPlatAsset(idFind);

        if (null != asset) {
            assetInfo = fillAssetBase(asset);
        }

        return assetInfo;
    }

    public SiAsset fillAssetBase(SiAssetEntity asset) {
        SiAsset siAsset = new SiAsset();

        siAsset.setId(asset.getId());
        siAsset.setRootId(asset.getRootId());
        siAsset.setCode(asset.getCode());
        siAsset.setType(asset.getType());
        siAsset.setSubType(asset.getSubType());
        siAsset.setClassify(asset.getClassify());
        siAsset.setLevel(asset.getLevel());
        siAsset.setName(asset.getName());
        siAsset.setState(asset.getState());
        siAsset.setProps(new SiProps());
        siAsset.setIsSystem(asset.getIsSystem());

        if (null != asset.getProps()) {
            asset.getProps().forEach((name, val) -> siAsset.getProps().put(name, val));
        }

        siAsset.setParentId(asset.getParentId());
        siAsset.getProps().put("updateTime", PluginUtil.formatDateTime(asset.getUpdateTime()));
        siAsset.getProps().put("createTime", PluginUtil.formatDateTime(asset.getCreateTime()));

        return siAsset;
    }

    private void fillAssetTree(SiAssetId idInfo, SiAsset asset) {
        if (StrUtil.isNotBlank(asset.getRootId())) {
            List<SiAssetEntity> roots = platAssetDao.getPlatAssetsByRootId(SiRootId
                    .builder()
                    .platformId(idInfo.getOwnerId())
                    .rootId(asset.getRootId())
                    .build());
            if (null != roots && !roots.isEmpty()) {
                List<SiAsset> assets = new ArrayList<>();
                roots.forEach(item -> assets.add(fillAssetBase(item)));
                handleSingleSubTree(asset, assets, null);
            }
        }
    }

    private void fillAssetRelation(SiAssetId idInfo, SiAsset assetInfo, List<SiAsset> children) {
        List<String> findIds = new ArrayList<>();
        List<SiAssetRelEntity> relList;

        relList = platAssetRelDao.getRelations(
                RelationIdInfo.builder()
                        .ownerId(idInfo.getOwnerId())
                        .assetId(assetInfo.getId())
                        .assetType(assetInfo.getType())
                        .build());

        findIds.add(assetInfo.getId());
        buildAssetRelation(idInfo, assetInfo, children, relList, findIds);
    }

    private void buildAssetRelation(SiAssetId idInfo,
                                    SiAsset asset,
                                    List<SiAsset> children,
                                    List<SiAssetRelEntity> relList,
                                    List<String> findIds) {
        if (relList != null && !relList.isEmpty()) {
            List<SiAssetEntity> plAssets;

            plAssets = platAssetDao.getPlatAssets(SiAssetIds.builder()
                    .ownerId(idInfo.getOwnerId())
                    .ids(relList.stream().map(SiAssetRelEntity::getAssetId)
                            .filter(id -> !findIds.contains(id))
                            .collect(Collectors.toList()))
                    .build());

            if (plAssets != null && !plAssets.isEmpty()) {
                plAssets.forEach(item -> {
                    children.add(fillAssetBase(item));
                });
            }
        }
    }
}
